//
// Created by Jinyu Zhu on 2022/4/18.
//

#ifndef PROVDB_TAG_GRAPH_BASIC_H
#define PROVDB_TAG_GRAPH_BASIC_H

// Assertions to Exit
#define Assert(Cond) \
  ((Cond) ? static_cast<void>(0) : ExeStop(NULL))

#define AssertR(Cond, Reason) ((Cond) ? static_cast<void>(0) : \
  ExeStop(Reason))

#include "stdexcept"
#include "algorithm"

void ExeStop(const char *MsgCStr);

// Smart Pointers
class TCRef {
private:
    int Refs;
private:
    TCRef &operator=(const TCRef &);
    TCRef(const TCRef &);

public:
    TCRef() : Refs(0) {}
    ~TCRef() {
        if (Refs != 0) { AssertR(Refs == 0, "de-construct when refs != 0"); }
    }
    void MkRef() { Refs++; }
    void UnRef() {
        Assert(Refs > 0);
        Refs--;
    }
    bool NoRef() const { return Refs == 0; }
    int GetRefs() const { return Refs; }
};

// Smart-Pointer-With-Reference-Count
template<class TRec>
class TPt {
public:
    typedef TRec TObj;
private:
    TRec *Addr;

    void MkRef() const {
        if (Addr != NULL) {
            Addr->CRef.MkRef();
        }
    }

    void UnRef() const {
        if (Addr != NULL) {
            Addr->CRef.UnRef();
            if (Addr->CRef.NoRef()) { delete Addr; }
        }
    }

public:
    TPt() : Addr(NULL) {}
    TPt(const TPt &Pt) : Addr(Pt.Addr) { MkRef(); }
    TPt(TRec *_Addr) : Addr(_Addr) { MkRef(); }
    ~TPt() { UnRef(); }
    TPt &operator=(const TPt &Pt) {
        if (this != &Pt) {
            Pt.MkRef();
            UnRef();
            Addr = Pt.Addr;
        }
        return *this;
    }
    bool operator==(const TPt &Pt) const { return Addr == Pt.Addr; }
    bool operator!=(const TPt &Pt) const { return Addr != Pt.Addr; }
    TRec *operator->() const {
        Assert(Addr != NULL);
        return Addr;
    }
    TRec &operator*() const {
        Assert(Addr != NULL);
        return *Addr;
    }
    TRec &operator[](const int &RecN) const {
        Assert(Addr != NULL);
        return Addr[RecN];
    }
    TRec *operator()() const { return Addr; }
    bool Empty() const { return Addr == NULL; }
    void Clr() {
        UnRef();
        Addr = NULL;
    }
    int GetRefs() const {
        if (Addr == NULL) { return -1; } else { return Addr->CRef.GetRefs(); }
    }
};

// Utils
template<class Iter, class T>
Iter binary_find(Iter begin, Iter end, T val)
{
    // Finds the lower bound in at most log(last - first) + 1 comparisons
    Iter i = std::lower_bound(begin, end, val);

    if (i != end && !(val < *i))
        return i; // found
    else
        return end; // not found
}
template<class V, class T>
void sorted_insert(V& vec, T& val)
{
    vec.insert(std::upper_bound(vec.begin(), vec.end(), val), val);
}
template<class V, class T>
void binary_delete_if_exists(V& vec, T& val)
{
    auto found = binary_find(vec.begin(), vec.end(), val);
    if (found != vec.end()) {
        vec.erase(found);
    }
}

#endif //PROVDB_TAG_GRAPH_BASIC_H
